#include "entitycontainer.h"

/**
 * 19.3.12 - Steven Hartin - Original Creation
 */

CEntityContainer* CEntityContainer::getInstance()
{
    /** 
     * @return CEntityContainer* The singleton instance of the container
     */
    static CEntityContainer s_instance;
    return &s_instance;
}

CEntityContainer::~CEntityContainer()
{
    /**
     * Destructor, clear all memory.
     */
    clearAllEntities();
}

CEntity *CEntityContainer::addEntity(CEntity *pEntity)
{
    /**
     * Adds an entity to be stored in the entity list.
     *
     * @param CEntity *pEntity The Entity to store in the container
     */
    if (m_arrEntities.find(pEntity->getID()) != m_arrEntities.end())
    {
        m_arrEntities.find(pEntity->getID())->second.push_back(pEntity);
    }
    else
    {
        vector<CEntity*> vecTemp;
        vecTemp.push_back(pEntity);
        m_arrEntities.insert(pair<ENTITY_ID, vector<CEntity*>>(
                                pEntity->getID(), vecTemp));
    }
    return pEntity;                                                             
}                          

void CEntityContainer::reset()
{
    /**
     * Resets all entities. This will essentially remove all entities which
     * do not belong on map load. These entities are the ones which are not
     * temporary. Reset any permanent entities. 
     */
    for (map<ENTITY_ID, vector<CEntity*>>::iterator pEntityGroup(
         m_arrEntities.begin()); pEntityGroup != m_arrEntities.end();
         ++pEntityGroup)
    {
        for (vector<CEntity*>::iterator pEntity(pEntityGroup->second.begin());
             pEntity != pEntityGroup->second.end(); )
        {
            if ((*pEntity)->isPermanent())
            {
                (*pEntity)->reset();
                ++pEntity;
            }
            else
            {
                delete *pEntity;
                pEntity = pEntityGroup->second.erase(pEntity);
            }
        }
    }

}

void CEntityContainer::tick()
{
    /**
     * Executed when we wish to tick through all the entities. Ensure if any
     * of the entities are set for delete, we remove them. Otherwise tick the
     * entity.
     */
    for (map<ENTITY_ID, vector<CEntity*>>::iterator pEntityGroup(
         m_arrEntities.begin()); pEntityGroup != m_arrEntities.end(); 
         ++pEntityGroup)
    {
        for (vector<CEntity*>::iterator pEntity(pEntityGroup->second.begin());
             pEntity != pEntityGroup->second.end();)
        {
            
            if ((*pEntity)->getDirtyBit())
            {
                delete *pEntity;
                pEntity = pEntityGroup->second.erase(pEntity);
                if (pEntityGroup->second.size() && 
                    pEntity != pEntityGroup->second.end())
                {
                    ++pEntity;
                }
            }
            else
            {
                ++pEntity;
            }
        }
    }

    for (map<ENTITY_ID, vector<CEntity*>>::iterator pEntityGroup(
         m_arrEntities.begin()); pEntityGroup != m_arrEntities.end();
         ++pEntityGroup)
    {
        for (vector<CEntity*>::iterator pEntity(pEntityGroup->second.begin());
             pEntity != pEntityGroup->second.end(); ++pEntity)
        {
            (*pEntity)->tick();
        }
    }    
}

bool CEntityContainer::removeEntityList(ENTITY_ID nKey)
{
    /** 
     * Removes all the entities from the list given by the key. Restores all
     * memory to the heap.
     *
     * @param ENTITY_ID  key The ID of the key to remove
     * @return bool Whether the remove was successful
     */
    map<ENTITY_ID, vector<CEntity*>>::const_iterator 
         pPos(m_arrEntities.find(nKey));
    if (pPos != m_arrEntities.end())
    {
        for (vector<CEntity*>::const_iterator pEntity(pPos->second.begin());
             pEntity != pPos->second.end(); ++pEntity)
        {
            delete *pEntity;
        }
        m_arrEntities.erase(pPos);

        return true;
    }
    return false;
}

bool CEntityContainer::removeEntity(CEntity *pEntity)
{
    /**
     * Removes an entity from the container. This actually doesn't do the 
     * removing, instead it alters the dirty bit to true. On the next iteration,
     * this will be recognised as being removed and remove it and clean up the
     * memory. The reason this doesn't directly remove it is that this function
     * is called in the tick function of the entities. If the entity is directly
     * removed, then it affects the iterators position in the tick loop.
     * 
     * @param CEntity *pEntity The entity to remove
     * @return bool Whether or not any entities were removed.
     */
    bool bEntityRemoved(false);
    for (map<ENTITY_ID, vector<CEntity*>>::iterator 
         pPos(m_arrEntities.begin()); pPos != m_arrEntities.end();
         ++pPos)
    {
        for (vector<CEntity*>::iterator pEnt(pPos->second.begin());
             pEnt != pPos->second.end(); ++pEnt)
        {
            if (*pEnt == pEntity)
            {
                pEntity->setDirtyBit();
                bEntityRemoved = true;
            }
        }
    }
    return bEntityRemoved;
}

vector<CEntity*> CEntityContainer::getEntityList(ENTITY_ID nKey) const
{
    /**
     * @param ENTITY_ID key The key of the entities to return
     * @return vector<CEntity*> A list of entities designated by the key
     */
    map<ENTITY_ID, vector<CEntity*>>::const_iterator 
         pPos(m_arrEntities.find(nKey));
    if (pPos != m_arrEntities.end())
    {
        return pPos->second;
    }
    // Return blank entity list
    vector<CEntity*> vecEntityList;
    return vecEntityList;
}

vector<CEntity*> CEntityContainer::getEntityList(string szKey) const
{
    /**
     * @param string key The key of the entities to return
     * @return vector<CEntity*> A list of entities designated by the key
     */
    for (map<ENTITY_ID, vector<CEntity*>>::const_iterator 
         pPos(m_arrEntities.begin()); pPos != m_arrEntities.end(); ++pPos)
    {
        if (pPos->second.size() >= 1)
        {
            if (pPos->second.at(0)->getKey() == szKey)
            {
                return pPos->second;
            }
        }
    }
    // Return blank entity list
    vector<CEntity*> vecEntityList;
    return vecEntityList;
}

void CEntityContainer::clearAllEntities()
{
    /**
     * Clears all entities from the list and clears the memory.
     */
    if (!m_arrEntities.size())
    {
        // Something bad happens if we try to loop through a blank array
        return;
    }
    for (map<ENTITY_ID, vector<CEntity*>>::const_iterator 
            pPos(m_arrEntities.begin()); pPos != m_arrEntities.end(); ++pPos)
    {
        for (vector<CEntity*>::const_iterator pEntity(pPos->second.begin());
                pEntity != pPos->second.end(); ++pEntity)
        {
            delete *pEntity;
        }
    }
    m_arrEntities.clear();
}